package au.com.carboncycle.gb.engine.entities;

import java.awt.Point;
import java.awt.Polygon;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.util.HashSet;
import java.util.Set;

import org.apache.log4j.Logger;

import au.com.carboncycle.gb.engine.GameClock;
import au.com.carboncycle.gb.engine.graphics.GBRenderer;

/**
 * Controls the physics and laws of the general brawler world.
 * 
 * @author Brendan
 *
 */
public class WorldEntity {
	
	private static final Logger log = Logger.getLogger(WorldEntity.class);
	
	
	private static final Point BAD_GUY_SPAWN_POINT_RIGHT = new Point(900, 400);

	
	
	final Sprite hero;
	final Set<BadGuySprite> badGuys;
	
	final Polygon heroBounds;
	
	public WorldEntity(Sprite heroSprite) {
		this.hero = heroSprite;
		this.badGuys = new HashSet<BadGuySprite>();
		this.heroBounds = new Polygon(new int[]{0, 60,1110,1050}, new int[]{420, 300, 300, 420}, 4);
	}


	public boolean heroPositionAllowed(Rectangle2D newHeroBounds) {
		if (log.isTraceEnabled())
			log.trace(newHeroBounds.toString() + " heroBounds contains: " + heroBounds.contains(newHeroBounds.getMinX(), newHeroBounds.getMinY()));
		
		//collision detection - TODO - extract method, to reuse for later
		for (Sprite badGuy : badGuys) {
			if (badGuy.getBounds().intersects(newHeroBounds)) { //TODO: they should be able to walk at the same Y coordinate as long as the bottom X coord of the 
																// sprites are the same
				return false;
			}
		}
		
		return heroBounds.contains(newHeroBounds.getMinX(), newHeroBounds.getMinY());
	}
	
	
	public boolean badGuyPositionAllowed(Rectangle2D badGuyBounds, BadGuySprite badGuyToTest) {
		if (log.isTraceEnabled())
			log.trace(badGuyBounds.toString() + " heroBounds contains: " + heroBounds.contains(badGuyBounds.getMinX(), badGuyBounds.getMinY()));
		
		if (hero.getBounds().intersects(badGuyBounds)) {
			return false;
		}
		
		//collision detection - TODO - extract method, to reuse for later
		for (Sprite badGuy : badGuys) {
			if (badGuyToTest != badGuy && badGuy.getBounds().intersects(badGuyBounds)) {
				return false;
			}
		}
		
		return heroBounds.contains(badGuyBounds.getMinX(), badGuyBounds.getMinY());
	}

	
	public void render(GBRenderer renderer) {
		//renderer.drawShape(heroBounds);
		if (log.isTraceEnabled()) {
			for (Sprite badGuy : badGuys) {
				renderer.drawShape(badGuy.getBounds());
				
				renderer.drawPath(getPathToHeroFrom(badGuy));
			}
			
			renderer.drawShape(hero.getBounds());
		}
	}
	
	public Path2D getPathToHeroFrom (Sprite otherSprite) {
		Path2D.Float path = new Path2D.Float(Path2D.WIND_EVEN_ODD);
		
		path.moveTo(otherSprite.position.getX(), otherSprite.position.getY());
		path.lineTo(hero.position.getX(), hero.position.getY());
		
		return path;
	}


	public void addBadGuy(BadGuySprite badGuySprite) {
		badGuys.add(badGuySprite);
	}

	public Set<BadGuySprite> getBadGuys() {
		return badGuys;
	}
	
	
	public void tick(GameClock gameClock) {
		ensureAppropriateAmountOfBadGuys(gameClock);
		
		for (BadGuySprite badGuy : badGuys) {
			badGuy.tick(gameClock);
		}
		
		
	}


	private void ensureAppropriateAmountOfBadGuys(GameClock gameClock) {
		if (notEnoughBadGuys() && timeIsRightForANewBadGuy(gameClock)) {
			addBadGuy (new BadGuySprite(BAD_GUY_SPAWN_POINT_RIGHT, this));
		}
	}


	private boolean timeIsRightForANewBadGuy(GameClock gameClock) {
		return gameClock.getGameTime() % 5 == 0;
	}


	private boolean notEnoughBadGuys() {
		return badGuys.isEmpty();
	}
	
	
	
	
	
}
